"""
A wrapper around the library Secret Rabbit Code (aka libsamplerate) by Erik Castro de Lopo
(Currently SRC version 0.1.7)
http://www.mega-nerd.com/SRC/index.html

licensed under the GPL
(http://www.gnu.org/copyleft/gpl.html)

Requires numpy and ctypes. Tested under windows only.
"""

try:
    import ctypes as ct
except ImportError:
    print "this module requires ctypes"
    import sys
    sys.exit(-1)
try:
    import numpy as np
except ImportError:
    print "this module requires numpy"
    import sys
    sys.exit(-1)

dllName = 'libsamplerate-0'

_lib=None
try:
    from ctypes.util import find_library
    #does the user already have libsamplerate installed?
    dllPath = find_library(dllName)
    _lib = ct.CDLL(dllPath)
except:
    try:
        #if not, get the dll installed with the wrapper
        import os
        dllPath = os.path.dirname(os.path.abspath(__file__))
        _lib = ct.CDLL(os.path.join(dllPath, dllName))
    except:
        import sys
        print "could not import libsamplerate dll"
        print "make sure the dll '%s' is in the path"%(dllName)
        sys.exit(-1)


#classes definitions
class CONVERTERS():
    SRC_SINC_BEST_QUALITY		= 0
    SRC_SINC_MEDIUM_QUALITY		= 1
    SRC_SINC_FASTEST			= 2
    SRC_ZERO_ORDER_HOLD			= 3
    SRC_LINEAR				= 4

class _SRC_DATA(ct.Structure):
    """
    data_in       : A pointer to the input data samples.
    input_frames  : The number of frames of data pointed to by data_in.
    data_out      : A pointer to the output data samples.
    output_frames : Maximum number of frames pointer to by data_out.
    src_ratio     : Equal to output_sample_rate / input_sample_rate.
    """
    _fields_ = [("data_in", ct.POINTER(ct.c_float)),
                ("data_out", ct.POINTER(ct.c_float)),
                ("input_frames", ct.c_long),
                ("output_frames", ct.c_long),
                ("input_frames_used", ct.c_long),
                ("output_frames_gen", ct.c_long),
                ("end_of_input", ct.c_int),
                ("src_ratio", ct.c_double),
               ]

#dll methods declarations
def _initLibMethods():
    _lib.src_get_name.restype = ct.c_char_p
    _lib.src_get_name.argtypes = [ct.c_int]
    _lib.src_get_description.restype = ct.c_char_p
    _lib.src_get_description.argtypes = [ct.c_int]
    _lib.src_get_version.restype = ct.c_char_p
    _lib.src_get_version.argtypes = None
    
    #simple API:
    #int src_simple (SRC_DATA *data, int converter_type, int channels) ;
    _lib.src_simple.restype = ct.c_int
    _lib.src_simple.argtypes = [ct.POINTER(_SRC_DATA), ct.c_int, ct.c_int]
_initLibMethods()

def src_simple(data, ratio, out=None, converter=CONVERTERS.SRC_LINEAR):
    """
    Performs 'simple' conversion of input. (correspond to the 'simple'
                                            API of SRC)
    Parameters:
    data        : a numpy array to convert, if the data is not of dtype
                  numpy.float32, then a local copy will be created
                  as SRC handles only float input at the moment
    ratio       : Equal to output_sample_rate / input_sample_rate
    out         : a numpy array in which to store the resampled data,
                  if None, a new array is created
    converter   : a CONVERTERS value
    
    returns:
    out         : the converted data with the dtype numpy.float32
    """
    nbChannels = data.size/data.shape[0]
    if out == None:
        out = np.empty((np.ceil(max(data.shape)*ratio).astype(int), nbChannels),
            dtype=np.float32)
    data_f = data
    if data.dtype!=np.float32:
        data_f = data.astype(np.float32)
    src_data = _SRC_DATA(data_in=data_f.ctypes.data_as(ct.POINTER(ct.c_float)),
                         data_out=out.ctypes.data_as(ct.POINTER(ct.c_float)),
                         input_frames=len(data),
                         output_frames=len(out),
                         src_ratio=ratio)
    _lib.src_simple(src_data, converter, nbChannels)
    return out
    
def src_version():
    return _lib.src_get_version()

if __name__=="__main__":    
    #generate 1s of a sin wave at 48000 Hz.
    freqOrig = 48000.
    t = np.linspace(0., 1, freqOrig)
    a = np.array(np.sin(2*np.pi*t*freqOrig), dtype=np.float32)
    
    #convert to a new sample rate
    freqResamp = 41000.
    print src_version()
    #ra = np.empty((freqResamp,), dtype=np.float32)
    #src_simple(a, freqResamp/freqOrig, out=ra)
    ra = src_simple(a, freqResamp/freqOrig)
    
    #generate the ground truth
    t = np.linspace(0., 1, freqResamp)
    truth = np.array(np.sin(2*np.pi*t*freqResamp), dtype=np.float32)
    
    print "absolute difference:",np.sum(np.abs(ra.flatten()-truth.flatten()))
    
    #plot the results
    import matplotlib.pyplot as plt
    plt.plot(np.arange(len(a)), a, label="original")
    plt.plot(np.arange(len(ra)), ra, label="resampled")
    plt.plot(np.arange(len(truth)), truth, label="ground truth")
    plt.legend()
    plt.show()